/*=====================================================================
  This file is part of a Microsoft SQL Server Shared Source Application.
  Copyright (C) Microsoft Corporation.  All rights reserved.
 
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
======================================================= */

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.AnalysisServices.AdomdClient;

namespace Microsoft.SqlServer.BISystems.Tool
{
    /// <summary>
    /// This class retrieves all candidate Tuples (from a Tuple Dictionary) for a token
    /// </summary>
    public class TupleDictionary
    {
        /// <summary>
        /// A dictionary that keeps all Tuples
        /// </summary>
        private static Dictionary<string, string[]> s_tupleDictionary;

        /// <summary>
        /// A connection.
        /// Note that we get information lazily so this will be used potentially thousands of times.
        /// </summary>
        private static AdomdConnection s_conn = null;
        private static AdomdCommand s_command = null;

        /// <summary>
        /// Disconnect and dispose of connection related objects.
        /// </summary>
        public static void Disconnect()
        {
            if (s_conn != null)
            {
                s_command.Dispose();
                s_conn.Close();
                s_conn.Dispose();
                s_conn = null;
                s_command = null;
            }
        }

        /// <summary>
        /// Clears tuple sets that are defined as depending on another.
        /// For example token b = "select ... |a|..."
        /// This should cause us to re-evaluate the tuple set for every query file we output.
        /// </summary>
        public static void ClearTupleSetForDependencies(Token token)
        {
            if (s_tupleDictionary == null)
                return;
            if (!(token.Tuple.Source == null || token.Tuple.Source == ""))
                return;

            // The MDX expression is actually in token.Tuple.Name.
            // Check the syntax for any token delimiters.
            if (ASQueryGenerator.HasTokenDelimiter(token.Tuple.Name))
            {
                // We found at least one token delimiter, so we assume the
                // MDX expression is functionally dependant upon the random 
                // selection set from some other token.
                // Therefore we must clear the set before every file.
                s_tupleDictionary.Remove(token.Name);
            }
        }

        /// <summary>
        /// Output cardinality statistics.
        /// </summary>
        public static void WriteCardinalityStatistics(ASQueryGenerator asg)
        {
            // First generate a sorted list so it is easier to read.
            SortedDictionary<string, int> sorteddict = new SortedDictionary<string, int>();
            foreach (KeyValuePair<string, string[]> kvp in s_tupleDictionary)
            {
                sorteddict.Add(kvp.Key, kvp.Value.Length);
            }

            Console.WriteLine("{0,11} {1,-20} {2}",
                "Cardinality",
                "Token",
                "DependsOn");
            foreach (KeyValuePair<string, int> kvp in sorteddict)
            {
                string strDependencyList = asg.GetTokenDependencyListAsStringFromTokenName(kvp.Key);
                strDependencyList = strDependencyList.Replace(ASQueryGenerator.TOKEN_SYMBOL.ToString(),"");
                Console.WriteLine("{0,11} {1,-20} {2}",
                    kvp.Value,
                    kvp.Key,
                    strDependencyList);
            }
        }


        /// <summary>
        /// There are two sources for getting all candidate tuples
        /// 1. Browse the cube dynamically
        /// 2. Browse a pre-generated list (stored in a text file)
        /// </summary>
        /// <param name="server"></param>
        /// <param name="cube"></param>
        /// <param name="database"></param>
        /// <param name="tokenKey"></param>
        /// <returns></returns>
        public static string[] RetrieveTuples(ASQueryGenerator asquerygen, string server, string cube, string database, Token token, System.Random rng)
        {
            // First look in the dictionary.
            if (s_tupleDictionary == null)
                s_tupleDictionary = new Dictionary<string, string[]>();
            string[] values = null;
            if (s_tupleDictionary.TryGetValue(token.Name, out values))
                return values;

            if (token.Tuple.Source == null || token.Tuple.Source == "")
                values = RetrieveTuplesDynamically(asquerygen, server, cube, database, token, rng);
            else
                values = RetrieveTuplesStatically(token.Name, token.Tuple.Source);

            // Add candidate tuples for this token to the dictionary
            // Note that key = token name, value = the array of tuple values.
            // E.g. key = |YearParameter|, value = { [2003], [2004], [2005] }.
            s_tupleDictionary.Add(token.Name, values);
            return values;
        }

        /// <summary>
        /// Statically retrieve all candiate Tuples for a token (by looking at the text file)
        /// </summary>
        /// <param name="server"></param>
        /// <param name="cube"></param>
        /// <param name="database"></param>
        /// <param name="tokenKey"></param>
        /// <returns></returns>
        public static string[] RetrieveTuplesStatically(string key, string source)
        {   
            string [] values = null;

            if (s_tupleDictionary == null)
			{
                s_tupleDictionary = new Dictionary<string, string[]>();
			}

            if ( s_tupleDictionary.TryGetValue(key, out values) == false )
			{
                // We haven't computed candidate Tuples for this token yet.
                StreamReader sr = null;
                ArrayList ar = new ArrayList();

                try
                {
                    sr = new StreamReader(source);
                                        
                    while (!sr.EndOfStream)
                    {
                        string temp = "(" + sr.ReadLine() + ")";

                        ar.Add(temp);
                    }

                    values = (string[])ar.ToArray(typeof(string));
                    
                    // Add candidate tuples for this token to the dictionary
                    s_tupleDictionary.Add(key, values);
			    }
			    catch (Exception e)
			    {
                    throw e;
			    }
			    finally
			    {
                    sr.Close();				 
			    }
            }

            return values;
        }

        /// <summary>
        /// Dynamically retrieve all candidate Tuples for a token (by querying cube)
        /// </summary>
        /// <param name="tuple"></param>
        /// <returns></returns>
        public static string[] RetrieveTuplesDynamically(ASQueryGenerator asquerygen, string server, string cube, string database, Token token, System.Random rng)
        {
            string [] values = null;

            if (string.Compare(server, "$Sample$", true) == 0)
            {
                // Special case to avoid connection and generate bogus tuples.
                // This is for testing query generation without connecting.

                values = new string[System.Math.Max(25, token.MaxQuantity)];
                for (int j = 0; j < values.Length; j++)
                {
                    values[j] = string.Format( "[{0}_{1:00}]", token.Name, j+1);
                }
            }
            else
			{
                // We haven't computed candidate Tuples for this token yet.

			    CellSet cs = null;
                ArrayList ar = new ArrayList();

			    try
			    {
                    // Check for recursive definitions, where the token's MDX expression depends on 
                    // the selected value of another token.
                    // Note that we do not check for recursion.
                    // Users must order tokens based on dependency.
                    string queryTokenDefinition = token.Tuple.Name;
                    if (ASQueryGenerator.HasTokenDelimiter(queryTokenDefinition))
                    {
                        queryTokenDefinition = asquerygen.GenerateQuery(queryTokenDefinition, rng);
                    }

                    if (s_conn == null)
                    {
                        s_conn = new AdomdConnection();
                        s_conn.ConnectionString = "Provider=MSOLAP;Data Source=" + server + ";Initial Catalog=" + database;
                        s_conn.Open();

                        s_command = s_conn.CreateCommand();
                    }

                    string mdx = "with member [measures].[__TESTTEST__] as 'null' select " + queryTokenDefinition + " on 0 from [" + cube + "] where [measures].[__TESTTEST__]";
                    s_command.CommandText = mdx;

				    cs = s_command.ExecuteCellSet();

				    Axis axis = cs.Axes[0];
				    Set set = axis.Set;
				    foreach (Tuple t in set.Tuples)
				    {
                        StringBuilder temp = new StringBuilder("(");
					    foreach (Member mem in t.Members)
					    {
                            if (temp.Length > 1)
                                temp.Append(",");
                            temp.Append(mem.Name);
					    }
                        temp.Append(")");
                        ar.Add(temp.ToString());
				    }

                    values = (string[])ar.ToArray(typeof(string));
			    }
			    catch (Exception e)
			    {
                    StringBuilder errMsg = new StringBuilder();
                    if (s_conn != null)
                    {
                        errMsg.Append("Connection String is: " + s_conn.ConnectionString);
                        errMsg.AppendLine();
                    }
                    if (s_command != null)
                    {
                        errMsg.Append("MDX query is: " + s_command.CommandText);
                        errMsg.AppendLine();
                    }

                    throw new Exception(errMsg.ToString(), e);
			    }
			    finally
			    {
				    cs = null;
			    }
            }

            return values;
        }
    }    
}
